#include <sstream>
#include <iterator>
#include "marshal.hpp"
#include "wrapper.hpp"


typedef std::ostreambuf_iterator<char> OutputIterator;
typedef std::istreambuf_iterator<char> InputIterator;

typedef redcell::OutputBuffer<OutputIterator> StringOutputBuffer;
typedef redcell::InputBuffer<InputIterator> StringInputBuffer;


struct StringWriterImpl {
  std::ostringstream stream;
  StringOutputBuffer buf;

  StringWriterImpl()
    : buf(OutputIterator(stream)) {
  }
};

struct StringReaderImpl {
  std::istringstream stream;
  StringInputBuffer buf;

  StringReaderImpl(const redcell::String raw)
    : stream(raw), buf(InputIterator(stream), InputIterator()) {
  }
};


StringWriter::StringWriter() {
  impl = new StringWriterImpl;
}

StringWriter::~StringWriter() {
  delete impl;
}

void StringWriter::putKeySkip(const size_t val) {
  impl->buf.putKeySkip(val);
}

#if REDCELL_STRINGKEYS
void StringWriter::putStrKey(const redcell::StrKey &val) {
  impl->buf.putStrKey(val);
}
#endif

void StringWriter::putNil() {
  impl->buf.putNil();
}

void StringWriter::putInteger(const redcell::Integer val) {
  impl->buf.putInteger(val);
}

void StringWriter::putReal(const redcell::Real val) {
  impl->buf.putReal(val);
}

void StringWriter::putString(const redcell::String &val) {
  impl->buf.putString(val);
}

void StringWriter::putEmptySub() {
  impl->buf.putEmptySub();
}

void StringWriter::putSub() {
  impl->buf.putSub();
}

void StringWriter::putEnd() {
  impl->buf.putEnd();
}

redcell::String StringWriter::str() const {
  return impl->stream.str();
}


StringReader::StringReader(const redcell::String raw) {
  impl = new StringReaderImpl(raw);
}

StringReader::~StringReader() {
  delete impl;
}

unsigned char StringReader::getFlag() throw (redcell::MarshalError) {
  return impl->buf.getFlag();
}

redcell::Integer StringReader::getInteger(const int sign) throw (redcell::MarshalError) {
  return impl->buf.getInteger(sign);
}

redcell::Real StringReader::getFloat() throw (redcell::MarshalError) {
  return impl->buf.getFloat();
}

redcell::Real StringReader::getDouble() throw (redcell::MarshalError) {
  return impl->buf.getDouble();
}

redcell::String StringReader::getString() throw (redcell::MarshalError) {
  return impl->buf.getString();
}
